import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.tree.ParseTree;
// org.antlr.v4.runtime.tree.ParseTree
import org.antlr.v4.runtime.tree.TerminalNode;


public class VisitHandler extends GoParserBaseVisitor<Integer> {

    enum Type{
        INT, FLOAT, STRING
    }
    enum Op{
        // unary_op
        UMINUS,
        // mul_op
        PLUS, SUB, MUL, DIV
    }

    public class Log{
        public Log la, ne;
        @Override
        public String toString() {
            return ("COMMON LOG：\n[last]="+la+"; \n[next]="+ne);
        }
    }

    public class HeaderLog extends Log{
        public String procName;
        public Log la, ne;
        HeaderLog(String name, Log la, Log ne){
            this.procName = name;
            this.la = la;
            this.ne = ne;
        }
        HeaderLog(HeaderLog log){
            this.procName = log.procName;
            this.la = log.la;
            this.ne = log.ne;
        }
        @Override
        public String toString() {
            return ("HEADER LOG：\n[headerName]="+procName+"; \n[last]="+la+"; \n[next]="+ne);
        }
    }

    public class IdentifierLog extends Log{
        public String name;
        public Log ptr_log;
        public Log la, ne;
        IdentifierLog(String name, Log pointer, Log la, Log ne){
            this.name = name;
            this.ptr_log = pointer;
            this.la = la;
            this.ne = ne;
        }
        IdentifierLog(IdentifierLog log){
            this.name = log.name;
            this.ptr_log = log.ptr_log;
            this.la = log.la;
            this.ne = log.ne;
        }
        @Override
        public String toString() {
            return ("IDENTIFER LOG：\n[name]="+name+"; \n[ptr_log]="+ptr_log+"; \n[last]="+la+"; \n[next]="+ne);
        }
    }

    public class DigitLog extends Log{
        public Type type;
        public String value;
        public Log la, ne;
        DigitLog(Type type, String value, Log la, Log ne){
            this.type = type;
            this.value = value;
            this.la = la;
            this.ne = ne;
        }
        DigitLog(DigitLog log){
            this.type = log.type;
            this.value = log.value;
            this.la = log.la;
            this.ne = log.ne;
        }
        @Override
        public String toString() {
            return ("DIGIT LOG：\n[type]="+type+"; \n[value]="+value+"; \n[last]="+la+"; \n[next]="+ne);
        }
    }

    public class OperationLog extends Log{
        public Op op;
        public Log ptr_log1, ptr_log2;
        public Log la, ne;
        OperationLog(Op op, Log arg1, Log arg2, Log la, Log ne){
            this.op = op;
            this.ptr_log1 = arg1;
            this.ptr_log2 = arg2;
            this.la = la;
            this.ne = ne;
        }
        OperationLog(OperationLog log){
            this.op = log.op;
            this.ptr_log1 = log.ptr_log1;
            this.ptr_log2 = log.ptr_log2;
            this.la = log.la;
            this.ne = log.ne;
        }
        @Override
        public String toString() {
            return ("OPERATION LOG：\n[op]="+op+
                    ";\n [ptr_log1]="+ptr_log1+
                    ";\n [ptr_log2]="+ptr_log2+
                    ";\n [last]="+la+
                    ";\n [next]="+ne);
        }
    }

    public class Table{
        enum LogType{
            HEADER_LOG, IDENTIFIER_LOG, DIGIT_LOG, OPERATION_LOG
        }

        public HeaderLog table_head = new HeaderLog("main", null, null);
        public Stack<Log> st = new Stack<>();
        // public HeaderLog pointer = table_head;
        public Log now = table_head;

        public boolean insertLog(Log log){
            // System.out.println("---------------------------- insert ----------------------------");
            // System.out.println(now.ne);
            now.ne = log;
            // System.out.println(now.ne);
            log.la = now;
            now = log;
            // System.out.println("---------------------------------------------------------------");
            return true;
        }

        public boolean deleteLog(Log log){
            if(log == null){
                return false;
            }
            if(log.ne == null){
                log.la.ne = null;
                log = null;
                return true;
            }
            else if(log.la == null){
                System.out.println("You are not allowed to delete the table header");
                return false;
            }
            else{
                log.la.ne = log.ne;
                log.ne.la = log.la;
                log = null;
            }
            return true;
        }

        public void printTable(){
            System.out.println("----------------------------Table----------------------------");
            Log tmp = table_head;
            while(tmp.ne != null){
                System.out.println(tmp);
                tmp = tmp.ne;
            }
            System.out.println("------------------------------------------------------------");
        }
    }

    Table table = new Table();   
    


    @Override
    public Integer visitShortVarDecl(GoParser.ShortVarDeclContext ctx) {
        System.out.println("ShortVarDecl " + ctx.getText());
        List<TerminalNode> nodes = ctx.identifierList().IDENTIFIER();
        for (int i = 0; i < nodes.size(); i ++){
            HeaderLog identifierExpression = new HeaderLog(nodes.get(i).getText(), 
                                                           null, null);
            IdentifierLog tmp = new IdentifierLog(nodes.get(i).getText(), identifierExpression, null, null);
            System.out.println("before insert");
            System.out.println(tmp);
            table.insertLog(tmp);
            System.out.println("after insert");
            System.out.println(tmp);
            table.printTable();
    }
        table.printTable();
        return visitChildren(ctx);
    }

    @Override
    public Integer visitExpression(GoParser.ExpressionContext ctx) {
        
        return super.visitExpression(ctx);
    }

}